#include<iostream>
#include<unistd.h>
#include<vector>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include"process_pool.hpp"
#include<errno.h>

using namespace std;

#define processnum 5

vector<tasks> task;

void task_load(vector<tasks>* task)
{
    task->push_back(task_1);
    task->push_back(task_2);
    task->push_back(task_3);
    task->push_back(task_4);
}

class process_channel
{
    public:
        process_channel(int cmdfd, pid_t slaverid, string& processname)
            :_cmdfd(cmdfd)
            ,_slaverid(slaverid)
            ,_processname(processname)
        {}

    public:
        int _cmdfd; //发送命任务的文件描述符
        pid_t _slaverid; //子进程的pid
        string _processname; //子进程的名字
};

void slaver()
{
    int cnt = 5;
    while(true)
    {
        int cmd_code = 0;
        int n = read(0, &cmd_code, sizeof(int)); //初始化时已经重定向到0
        if(n == sizeof(cmd_code))
        {
            printf("child:%d gets a command from father:%d, command_code:%d\n", getpid(), getppid(), cmd_code);
            if(cmd_code >= 0 && cmd_code < 4)
                task[cmd_code-1]();
        }
        else
        {
            //perror("slaver,n==0");
            break;
        }
        cnt--;
        if(cnt == 0) break;
        sleep(1);
    }

}

int init_channels(vector<process_channel>* channels) //输出型参数
{
    task_load(&task);
    vector<int> oldfd; //将父进程的写通道记录下来
    for(int i=0; i<processnum; i++)
    {
        int pipefd[2];
        int ret = pipe(pipefd);
        //assert(ret<0);
        if(ret < 0) return -1;
        oldfd.push_back(pipefd[1]); //记录父进程写通道
        pid_t id = fork();
        //sleep(1);
        if(id<0) return -1;
        if(id == 0)
        {
            //child
            //close(pipefd[1]); //关闭子进程的写通道
            int n = dup2(pipefd[0], 0); //将读通道重定向至0通道
            if(n < 0)
            {
                perror("dup2");
                break;
            }
            close(pipefd[0]); //关闭原来的读通道
            //cout << "close the redundancy:";
            for(auto& i : oldfd) //关闭继承下来的所有写通道
            {
                close(i);
                //cout << i << " ";
            }
            cout << endl;
            //cout << "This is a child process - " << getpid() << " read:" << pipefd[0] << " ppid:" << getppid() << endl;
            slaver(); //子进程处理任务
            exit(0);
        }
        //father
        close(pipefd[0]); //关闭父进程的读通道
        string name = "process-" + to_string(i);
        channels->push_back(process_channel(pipefd[1], id, name));
    }
}

void debug(const vector<process_channel>& channels) //输入型参数
{
    for(auto& c : channels)
    {
        cout<< c._cmdfd << " " << c._slaverid <<  " " << c._processname << endl;
    }
}

void menu()
{
    cout << "---------------------------------------------------" << endl;
    cout << "1: 任务1   2:任务2    3:任务3    4:任务4   0:退出" << endl;
}

void ctrl_slavers(const vector<process_channel>& channels)
{
    //srand(time(nullptr)^1024); //种下一颗随机种子
    //int cnt = 5;
    int tasknum = 0;
    int which = 0;
    while(true)
    {
        cout << endl;
        menu();
        cout << "请输入需要执行的任务：";
        cin >> tasknum;
        if(tasknum == 0)
        {
            break;
        }
        //cnt--;
        //选择任务
        //int cmd_code = rand()%task.size();

        //选择进程
        //int channel = rand()%channels.size();
        printf("father:%d has sent a command:%d to child:%d\n", getpid(), tasknum, channels[which]._slaverid);

        //发送任务
        ssize_t n = write(channels[which]._cmdfd, &tasknum, sizeof(tasknum));
        if(n < 0)
        {
            perror("write");
            break;
        }
        //if(cnt == 0) break;
        which++;
        which %= channels.size();
        sleep(1);
    }

}

void process_off(const vector<process_channel>& channels)
{
    //方法一：倒着关闭通道与等待子进程
    // int i = channels.size()-1;
    // for(i; i>=0; i--)
    // {
    //     close(channels[i]._cmdfd);
    //     int n = waitpid(channels[i]._slaverid, nullptr, 0);
    //     if(n < 0)
    //     {
    //         string error = "waitpid:" + to_string(channels[i]._slaverid);
    //         perror(error.c_str());
    //         break;
    //     }
    // }

    //方法二：每次创建子进程后就立刻关闭继承下来的多余的通道
    for(auto& c : channels)
    {
        close(c._cmdfd);
        int n = waitpid(c._slaverid, nullptr, 0);
        if(n < 0)
        {
            string error = "waitpid:" + to_string(c._slaverid);
            perror(error.c_str());
            break;
        }
        else
        {
            printf("wait success:%d\n", c._slaverid);
        }
        sleep(1);
    }
}



int main()
{
    vector<process_channel> channels;
    
    //1.初始化子进程
    int ret = init_channels(&channels);
    if(ret<0) return -1;
    //debug(channels);

    //2.控制子进程
    ctrl_slavers(channels);

    //3.收尾工作
    process_off(channels);

    return 0;
}